#pragma once

#include "ColligereSingleton.hpp"
#include "ColligereUtils.hpp"

namespace col 
{

class ControlLink
{
public:
	struct Type
	{
		enum
		{
			NormalInputField = 1,
			ListCtrl,
			PropertyGrid
		};
	};

	ControlLink() : mCtrlColIdx(-1), mType(0), mId(-1) { }
	
	//! Constructor for a normal input field control link.
	ControlLink ( const wxString &rXrcId ) : mXrcId ( rXrcId ), 
		mCtrlColIdx (-1), mType ( Type::NormalInputField ), 
		mId ( XRCID ( rXrcId.c_str() ) ) { }

	//! Constructor for a property grid control link.
	ControlLink ( wxUint32 propGridId, const wxString &rPropName ) 
		: mId ( propGridId ), mPropName ( rPropName ),
		mCtrlColIdx(-1), mType ( Type::PropertyGrid ) 
	{
		wxASSERT ( !mPropName.empty() );
	}
	
	//! Constructor for a list control control link.
	ControlLink ( const wxString &rXrcId, wxUint32 ctrlColIdx ) 
		: mId ( XRCID ( rXrcId.c_str() ) ), mXrcId ( rXrcId ),
		mType ( Type::ListCtrl ), mCtrlColIdx ( ctrlColIdx )
	{
		wxASSERT ( mCtrlColIdx > -1 );
	}

	const wxString &GetXrcId() const { return mXrcId; }
	wxInt32 GetId() const { return mId; }
	wxInt32 GetColumnIndex() const { return mCtrlColIdx; }
	wxUint32 GetType() const { return mType; }
	const wxString &GetPropName() const { return mPropName; }

	bool IsXrcCtrl() const { return !mXrcId.empty(); }
private:
	wxString mXrcId, mPropName;
	wxInt32 mCtrlColIdx, mId;
	wxUint32 mType;
};

//! This class represents a column in an SQL database table.
class Column
{
public:
	//! Default constructor.
	Column();

	Column ( wxUint32 indx, const wxString &rName, wxUint32 type, 
		wxUint32 constraints = 0 );

	//! This is only used with std::set find.
	Column ( wxUint32 indx ) : mIndx ( indx ) { }

	//! Constraint enum wrapper.
	struct Constraint
	{
		enum
		{
			NotNull			= 1 << 1,
			PrimaryKey		= 1 << 2,
			Unique			= 1 << 3,
			AutoIncrement	= 1 << 4
		};
	};

	//! Type enum wrapper.
	struct Type
	{
		enum { Integer, Text };
	};

	bool operator < ( const Column &rCol ) const;
	bool operator == ( wxInt32 colIndex ) const;
	bool operator == ( const wxString &rName ) const;
	bool operator == ( const Column &rCol ) const;

	const wxString &GetName() const;
	const ControlLink &GetCtrlLink() const;
	
	wxInt32 GetConstraints() const;
	wxInt32 GetType() const;
	wxInt32 GetIndex() const;

	void SetControlLink ( const ControlLink &rLink ) { mCtrlLink = rLink; }

	bool HasConstraint ( wxUint32 constraint ) const;

	const wxString ToString() const;

	bool IsIdColumn() const;
	bool IsParentIdColumn() const;
	bool IsTimestampColumn() const;
	bool IsInternalColumn() const;
	bool IsInvalid() const;
private:
	const wxString FormatConstraintsToString() const;
	const wxString FormatTypeToString() const;

	wxInt32 mIndx, mType, mConstraints;
	ControlLink mCtrlLink;
	wxString mName;
};

typedef std::set < Column > ColumnSet;

//! This class represents a trigger in an SQL database table.
class Trigger
{
public:
	Trigger ( const wxString &rName, wxUint32 eventType, 
		wxUint32 execPosition, const wxString &rTriggerSteps, 
		const wxString &rParentTable );

	//! Execution position enum wrapper.
	struct ExecPosition
	{
		enum { After, Before };
	};
	//! Even type enum wrapper.
	struct EventType
	{
		enum { Delete, Insert, Update, UpdateOf };
	};

	//! Returns the name of the trigger.
	const wxString &GetName() const;
	//! Returns the name of the parent table for this trigger.
	const wxString &GetParentTableName() const;
	//! Returns the SQL part which will be executed when this trigger is triggered.
	const wxString &GetTriggerSteps() const;

	//! Returns the event type for this trigger.
	wxUint32 GetEventType() const;
	//! Returns the execution position for this trigger.
	wxUint32 GetExecPosition() const;

	//! Returns the information from this trigger as an valid SQL CREATE TRIGGER query.
	const wxString ToSqlCreateTriggerQuery();

	bool operator == ( const Trigger &rCmp ) const;
private:
	//! The name of the trigger.
	wxString mName;
	//! The name of the parent database table.
	wxString mParentTableName;
	//! The SQL part which will be executed when this trigger is triggered.
	wxString mTriggerSteps;

	//! The event type for this trigger.
	wxUint32 mEventType;
	//! The execution position for this trigger.
	wxUint32 mExecPosition;
};



/*! @brief This class represents an SQL database table.
 **/
class DatabaseTable
{
public:
	//! Default constructor.
	DatabaseTable();

	struct AutoColumns
	{
		enum
		{
			None				= 0,
			Id					= 1 << 1,
			ParentId			= 1 << 2,
			TimestampUpdated	= 1 << 3,
			TimestampAdded		= 1 << 4,
			Trashed				= 1 << 5
		};
	};

	/*! @brief Constructor
	 *
	 * @see Create
	 **/
	DatabaseTable ( const wxString &rName, wxUint32 ownerDatabaseTypeId, 
		const wxString &rParentTableName = wxString(), 
		wxUint32 autoColSettings = AutoColumns::Id );

	/*! @brief Creates the table for two-step construction. 
	 *
	 * @param rName The name of the table.
	 *
	 * @param ownerDatabaseTypeId The type ID of the owner database.
	 *
	 * @param rParentTableName The name of the parent table
	 *        (e.g. the table "albums" is a child table of the "artists" table).
	 *
	 * @param autoColSettings Defines which columns will be created automatically for this table.
	 *                        The default is to create only the ID column.
	 **/
	void Create ( const wxString &rName, wxUint32 ownerDatabaseTypeId, 
		const wxString &rParentTableName = wxString(), 
		wxUint32 autoColSettings = AutoColumns::Id );

	/**!
	 * @param index The index of the column.
	 * @return The name of the column or an empty string if there's no such column.
	 **/
	const wxString &GetColumnNameByIndex ( wxUint32 index ) const;
	/**!
	 * @param rName The name of the column.
	 * @return The index of the column or wxNOT_FOUND if there's no such column.
	 **/
	wxInt32 GetColumnIndexByName ( const wxString &rName ) const;

	//! Replaces the current ORDER BY columns with new ones.
	void SetDefaultOrderBy ( const std::deque < wxUint32 > &rColOrder );

	/*! @brief Finalizes the table.
	 *
	 * Must be called *after* all columns are registered. Will take care of 
	 * registering the automatic columns.
	 **/
	void Finalize();

	/*! @brief Registers a new column to the column list.
	 *
	 * @param rCol The new column
	 * @return *this
	 **/
	DatabaseTable &RegisterColumn ( const Column &rCol );
	/*! @brief Registers a new trigger to the trigger list.
	 *
	 * @param rTr The new trigger
	 * @return *this
	 **/
	DatabaseTable &RegisterTrigger ( const Trigger &rTr );

	//! @see RegisterColumn
	DatabaseTable &operator += ( const Column &rCol );
	//! @see RegisterTrigger
	DatabaseTable &operator += ( const Trigger &rCol );

	bool operator == ( const DatabaseTable &rCmp ) const;

	//! Returns the name of the table.
	const wxString &GetName() const;
	//! Returns the name of the parent table.
	const wxString &GetParentName() const;
	/*! @brief Returns the item name.
	 *
	 * Returns the singular version of the table name (assuming that
	 * all table names are in the plural form (i.e. artists, albums)).
	 **/
	const wxString GetItemName() const;
	const Column &GetColumn ( wxUint32 index ) const;
	Column &GetColumn ( wxUint32 index );

	size_t GetColumnCount() const;
	size_t GetTriggerCount() const;
	wxUint32 GetOwnerDatabaseTypeId() const;
	const Column &GetParentIdColumn() const;
	const Column &GetIdColumn() const;
	//! Returns the list of columns for this table.
	const ColumnSet &GetColumns() const;

	//! Returns a pointer to the column that has the name 'rName' or 0 if not found.
	const Column *FindColumn ( const wxString &rName ) const;

	/*! @brief Checks if this column has an parent ID column
	 * 
	 * @return True if it has, false otherwise.
	 **/
	bool HasParentIdColumn() const;

	const wxString ToSqlCreateTableQuery ( bool includeTriggers = true );
	const wxString ToSqlSelectAllQuery ( std::deque < wxUint32 > colOrderBy = 
		std::deque < wxUint32 >() ) const;
	const wxString ToSqlSelectAllQuery ( wxUint32 parentDbId,
		std::deque < wxUint32 > colOrderBy = std::deque < wxUint32 >() ) const;
private:
	typedef std::deque < Trigger > TriggerDeque;

	/*!
	 * A default deque of column indexes that will be used to construct 
	 * the ORDER BY clause.
	 **/
	std::deque < wxUint32 > mDefaultOrderBy;

	//! A set of columns for this table.
	ColumnSet mColumns;
	//! A deque of triggers for this table.
	TriggerDeque mTriggers;
	//! The name of this table (must not be empty).
	wxString mName;
	/*! @brief The name of the parent table for this table. 
	 *
	 * Can be empty. Used in creating triggers.
	 **/
	wxString mParentTableName;

	/*! @brief A Bit field for the auto column settings.
	 *
	 * @see AutoColumns
	 **/
	wxUint32 mAutoColSettings;
	wxUint32 mOwnerDatabaseTypeId;

	static Column msInvalidColumn;
};

typedef std::deque < DatabaseTable > DatabaseTableDeque;

//! A class for handling table instances. 
class DatabaseTableListHandler : public Singleton < DatabaseTableListHandler >
{
public:
	virtual ~DatabaseTableListHandler() { }

	//! Registers a table to the table list.
	void RegisterTable ( const DatabaseTable &rTable );

	//! Returns all tables for the database with the type ID of 'dbType'.
	const DatabaseTableDeque GetTableList ( wxUint32 dbType ) const;
	/*! @brief Tries to retrieve a table.
	 *
	 * @param dbType The type ID of the database that should have this table.
	 * @param rName The name of the table.
	 * @param rTo A reference to the instance that will hold the table information.
	 * @return true if the table was found, false otherwise.
	 **/
	bool GetTable ( wxUint32 dbType, const wxString &rName, DatabaseTable &rTo ) const;
	//! Returns a pointer to the table if found, 0 otherwise.
	DatabaseTable *GetTable ( wxUint32 dbType, const wxString &rName );
	//! Returns all registered tables for all database types.
	const DatabaseTableDeque &GetAllTables() const { return mTableList; }
private:
	friend class Singleton < DatabaseTableListHandler >;

	//! A deque of all registered tables.
	DatabaseTableDeque mTableList;
};

}